/*********************************************************************************
 *      Copyrighu:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *     Filename:  main.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(20/03/25)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "20/03/25 16:30:52"
 *                 
 ********************************************************************************/

#include "main.h"
#include "struct.h"

int main(int argc,char **argv)
{
	char                    *progname = NULL;     
    int                     serv_port = 0;
    int                     daemon_run = 0;
	int                     opt;
	int                     rv;
	int                     listenfd;
	int                     connfd;
	int                     i;
	char                    buf[1024];
	char                    *token = NULL;
	socket_ser              sock;

	int                     epollfd;
	struct epoll_event      event;
	struct epoll_event      event_array[MAX_EVENTS];
	int                     events;

	data_t					data = {0};
	parse_proc_t             parse_proc = parse_data;

	char                    *logfile = "running_ser.log";
	char                    loglevel = LOG_LEVEL_INFO;

	struct                  option long_options[]=
	{
		{"port",required_argument,NULL,'p'},
		{"background",no_argument,NULL,'b'},
		{"debug", no_argument, NULL, 'd'},
		{"Pack", required_argument, NULL, 'P'},
		{"Stop", no_argument, NULL, 'S'},
		{"help",no_argument,NULL,'h'},
		{NULL,0,NULL,0}
	};

	progname = basename(argv[0]);     /*用basename提取程序名称*/

	while((opt = getopt_long(argc, argv,"p:bdP:Sh", long_options, NULL)) != -1)    /*用于解析命令行参数*/
	{
		switch(opt)
		{
			case 'p':
				serv_port=atoi(optarg);
				break;

			case 'b':
				daemon_run=1;
			    break;

			case 'd':
				logfile = "stdout";
				loglevel = LOG_LEVEL_DEBUG;
				break;

			case 'P':
				if( strcmp(optarg, "json") == 0 )
				{
					parse_proc = parse_data_json;
				}
				else if( strcmp(optarg, "tlv") == 0 )
				{
					parse_proc = parse_data_tlv;
				}
				else
				{
					usage_print(argv[0]);
				}
				break;

			case 'S':
				stop_background_running(PIDFILE);
				return 0;

			case 'h':
				usage_print(progname);
				return EXIT_SUCCESS;     /*定义的一个宏，值为0，表示程序成功退出*/
			
			default:
				break;
		}
	}

	if(!serv_port)
	{
		usage_print(progname);
		return -1;
	}

	socket_rlimit();

	if(logger_init(logfile, loglevel)<0)    /*初始化日志文件的函数，输出到server_run.log的文件中，而不是标准输出到屏幕上，这个文件的级别是debug*/
	{
		fprintf(stderr,"initial logger system failure\n");
		log_error("initial logger system failure\n");
		return -2;
	}

    signal(SIGINT,stop);    /*用户按下Ctrl+C，操作系统会向程序发送SIGINT信号。默认行为是终止程序，并且执行stop*/
	signal(SIGTERM, stop);  /*当系统关闭或使用 kill 命令终止程序时，操作系统会发送 SIGTERM 信号。通常用于优雅地终止程序，并且执行stop*/

	/*socket服务器初始化 */
	if( (listenfd = socket_server(NULL, serv_port)) < 0 )
	{
		dbg_print("ERROR: %s server listen on port %d failure\n", argv[0],serv_port);
		return -3;
	}
	dbg_print("Start to listen on port[%d]\n",serv_port);
	
	if( daemon_run )   /*守护进程是否后台运行*/
	{
		daemon(0, 0);
	}

	if( (epollfd = epoll_create(MAX_EVENTS)) < 0 )    /*生成epoll专用文件描述符*/
	{
		log_error("epoll_create() failure: %s\n", strerror(errno));
		return -4;
	}
	event.events = EPOLLIN;        
	event.data.fd = listenfd;
			
	if( epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) < 0)   /*添加listenfd*/
	{
		log_error("epoll add listen socket failure: %s\n", strerror(errno));
		return -5;
	}
   
	/* 创建库 */ 
	if(database_init() < 0)
	{
		goto CleanUp;
	}

	while(!g_stop)      /*当条件为假，或者0就一直循环，如果用户自己终止程序，或者操作系统终止程序就执行stop函数把信号变为1，退出循环， */
	{
		events=epoll_wait(epollfd, event_array, MAX_EVENTS, -1);     /*设置等待事件,第一个参数是创造epoll的返回值，第二：结构体数组，第三：数组中包含的元素个数，第四：设为-1，一直阻塞，直到有列表中关心的文件描述符产生 */
		if(events < 0)
		{
			log_error("epoll failure: %s\n",strerror(errno));
			break;
		}
		else if(events == 0)   /*表示超时 */
		{
			log_warn("epoll get timeout\n");
			continue;
		}

		for(i=0; i<events; i++)  /*遍历找是哪个文件描述符，哪个事件发生了 */
		{
			if((event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP))
			{
				log_error("epoll_wait get error on fd[%d]: %s\n",event_array[i].data.fd, strerror(errno));
				epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}

			if(event_array[i].data.fd == listenfd)
			{
				if((connfd = accept(listenfd,(struct sockaddr *)NULL,NULL)) < 0)
				{
					log_error("accept new client failure: %s\n",strerror(errno));
					continue;
				}

				event.data.fd = connfd;
				event.events = EPOLLIN;

				if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0)
				{
					log_error("epoll add socket failure: %s\n",strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				dbg_print("epoll add new client socket[%d] ok.\n",connfd);
			}
			
			else
			{
				memset(buf,0,sizeof(buf));
				rv = read(event_array[i].data.fd, buf, sizeof(buf));
				if(rv <= 0)
				{
					log_warn("socket[%d] read failure or get disconnect and will be removed.\n",event_array[i].data.fd);
					epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd,NULL);
					close(event_array[i].data.fd);
					continue;
				}

				else
				{
					log_info("socket[%d] read get %d btyes data.\n", event_array[i].data.fd, rv);
					
					if ( parse_proc((uint8_t *)buf, &data) == 0 )
					{
						if ( database_table(data.sn) == 0 )
						{
							if ( !database_safe_insert(&data) )
							{
								write(event_array[i].data.fd, "server have got data", sizeof("server have got data"));
							}
						}
					}
				}
			}
		}
	}

CleanUp:
	database_term();
	close(listenfd);
	unlink(PIDFILE);
	return 0;
}

void stop(int signum)
{
	(void)signum;
	g_stop = 1;
}
